<FrameworkSwitchCourse {fw} />

# Dịch máy

{#if fw === 'pt'}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter7/section4_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter7/section4_pt.ipynb"},
]} />

{:else}

<DocNotebookDropdown
  classNames="absolute z-10 right-0 top-0"
  options={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/vi/chapter7/section4_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/vi/chapter7/section4_tf.ipynb"},
]} />

{/if}

Bây giờ chúng ta hãy đi sâu vào dịch máy. Đây là một [tác vụ chuỗi sang chuỗi](/course/chapter1/7), có nghĩa là đây là một vấn đề có thể được hình thành như đi từ một chuỗi này sang chuỗi khác. Theo nghĩa đó, vấn đề khá giống với [tóm tắt](/course/chapter7/6) và bạn có thể điều chỉnh những gì chúng ta sẽ thấy ở đây thành các vấn đề chuỗi sang chuỗi khác như:

- **Chuyển văn phong**: Tạo mô hình *dịch* văn bản được viết theo một phong cách nhất định sang một phong cách khác (ví dụ: từ trang trọng sang thông thường hoặc tiếng Anh Shakespearean sang tiếng Anh hiện đại)
- **Hỏi đáp chung**: Tạo một mô hình tạo câu trả lời cho các câu hỏi, dựa trên ngữ cảnh

<Youtube id="1JvfrvZgi6c"/>

Nếu bạn có một kho văn bản đủ lớn với hai (hoặc nhiều) ngôn ngữ, bạn có thể huấn luyện một mô hình dịch mới từ đầu giống như chúng ta sẽ làm trong phần [lập mô hình ngôn ngữ nhân quả](/course/chapter7/6). Tuy nhiên, sẽ nhanh hơn nếu tinh chỉnh mô hình dịch hiện có, có thể là mô hình đa ngôn ngữ như mT5 hoặc mBART mà bạn muốn tinh chỉnh cho phù hợp với một cặp ngôn ngữ cụ thể hoặc thậm chí là một mô hình chuyên dụng để dịch từ ngôn ngữ này sang ngôn ngữ khác mà bạn muốn tinh chỉnh để phù hợp với kho dữ liệu cụ thể của mình.

Trong phần này, chúng ta sẽ tinh chỉnh mô hình Marian được huấn luyện trước để dịch từ tiếng Anh sang tiếng Pháp (vì rất nhiều nhân viên của Hugging Face nói cả hai ngôn ngữ đó) trên [tập dữ liệu KDE4](https://huggingface.co/datasets/kde4 ), là tập dữ liệu các tệp được bản địa hóa cho [ứng dụng KDE](https://apps.kde.org/). Mô hình chúng ta sẽ sử dụng đã được huấn luyện trước trên một kho dữ liệu lớn gồm các văn bản tiếng Pháp và tiếng Anh được lấy từ [Tập dữ liệu Opus](https://opus.nlpl.eu/), thực sự chứa tập dữ liệu KDE4. Nhưng ngay cả khi mô hình huấn luyện trước mà chúng ta sử dụng đã nhìn thấy dữ liệu đó trong quá trình huấn luyện trước của nó, chúng ta sẽ thấy rằng ta có thể nhận được phiên bản tốt hơn của nó sau khi tinh chỉnh.

Sau khi hoàn thành, chúng ta sẽ có một mô hình có thể đưa ra các dự đoán như sau:

<iframe src="https://course-demos-marian-finetuned-kde4-en-to-fr.hf.space" frameBorder="0" height="350" title="Gradio app" class="block dark:hidden container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

<a class="flex justify-center" href="/huggingface-course/marian-finetuned-kde4-en-to-fr">
<img class="block dark:hidden lg:w-3/5" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/modeleval-marian-finetuned-kde4-en-to-fr.png" alt="One-hot encoded labels for question answering."/>
<img class="hidden dark:block lg:w-3/5" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/modeleval-marian-finetuned-kde4-en-to-fr-dark.png" alt="One-hot encoded labels for question answering."/>
</a>

Như trong các phần trước, bạn có thể tìm thấy mô hình thực tế mà chúng ta sẽ huấn luyện và tải lên Hub bằng cách sử dụng đoạn mã bên dưới và kiểm tra kỹ các dự đoán của nó [tại đây](https://huggingface.co/huggingface-course/marian-finetuned-kde4-en-to-fr?text=This+plugin+allows+you+to+automatically+translate+web+pages+between+several+languages.).

## Chuẩn bị dữ liệu

Để tinh chỉnh hoặc huấn luyện một mô hình dịch từ đầu, chúng ta sẽ cần một tập dữ liệu phù hợp với tác vụ. Như đã đề cập trước đây, chúng ta sẽ sử dụng [tập dữ liệu KDE4](https://huggingface.co/datasets/kde4) trong phần này, nhưng bạn có thể điều chỉnh đoạn mã để sử dụng dữ liệu của riêng mình khá dễ dàng, miễn là bạn có các cặp của các câu bằng hai ngôn ngữ mà bạn muốn dịch từ và tới. Tham khảo lại [Chương 5](/course/chapter5) nếu bạn cần lời nhắc về cách tải dữ liệu tùy chỉnh của mình trong `Dataset`.

### Bộ dữ liệu KDE4

Như thường lệ, chúng ta tải xuống tập dữ liệu của mình bằng cách sử dụng hàm `load_dataset()`:

```py
from datasets import load_dataset

raw_datasets = load_dataset("kde4", lang1="en", lang2="fr")
```

Nếu bạn muốn làm việc với một cặp ngôn ngữ khác, bạn có thể chỉ định chúng bằng đoạn mã của chúng. Có tổng số 92 ngôn ngữ có sẵn cho bộ dữ liệu này; bạn có thể thấy tất cả chúng bằng cách mở rộng các thẻ ngôn ngữ trên [thẻ dữ liệu](https://huggingface.co/datasets/kde4) của nó.

<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/language_tags.png" alt="Language available for the KDE4 dataset." width="100%">

Hãy xem tập dữ liệu:

```py
raw_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['id', 'translation'],
        num_rows: 210173
    })
})
```

Chúng ta có 210,173 cặp câu, nhưng chỉ trong một lần tách, vì vậy chúng ta sẽ cần tạo bộ kiểm định của riêng mình. Như chúng ta đã thấy trong [Chương 5](/course/chapter5), `Dataset` có phương thức `train_test_split()` có thể giúp chúng ta. Chúng ta sẽ cung cấp một hạt giống (seed) cho khả năng tái tạo:

```py
split_datasets = raw_datasets["train"].train_test_split(train_size=0.9, seed=20)
split_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['id', 'translation'],
        num_rows: 189155
    })
    test: Dataset({
        features: ['id', 'translation'],
        num_rows: 21018
    })
})
```

Bạn có thể đổi `"test"` thành `"validation"` như sau:

```py
split_datasets["validation"] = split_datasets.pop("test")
```

Bây giờ chúng ta hãy xem xét một phần tử của tập dữ liệu:

```py
split_datasets["train"][1]["translation"]
```

```python out
{'en': 'Default to expanded threads',
 'fr': 'Par défaut, développer les fils de discussion'}
```

Chúng ta nhận được một từ điển có hai câu bằng cặp ngôn ngữ mà ta yêu cầu. Một điểm đặc biệt của bộ dữ liệu đầy đủ các thuật ngữ khoa học máy tính kỹ thuật này là chúng đều được dịch hoàn toàn bằng tiếng Pháp. Tuy nhiên, các kỹ sư Pháp thường lười biếng và để lại hầu hết các từ chuyên ngành khoa học máy tính bằng tiếng Anh khi họ nói chuyện. Ví dụ, ở đây, từ "threads" có thể xuất hiện trong một câu tiếng Pháp, đặc biệt là trong một cuộc trò chuyện kỹ thuật; nhưng trong tập dữ liệu này, nó đã được dịch thành đúng hơn là "fils de discussion". Mô hình huấn luyện trước mà chúng ta sử dụng, đã được huấn luyện trước trên một kho ngữ liệu lớn hơn của các câu tiếng Pháp và tiếng Anh, có tùy chọn dễ dàng hơn là để nguyên từ như sau:

```py
from transformers import pipeline

model_checkpoint = "Helsinki-NLP/opus-mt-en-fr"
translator = pipeline("translation", model=model_checkpoint)
translator("Default to expanded threads")
```

```python out
[{'translation_text': 'Par défaut pour les threads élargis'}]
```

Một ví dụ khác về hành vi này có thể được nhìn thấy với từ "plugin", đây không phải là một từ chính thức trong tiếng Pháp nhưng hầu hết người bản ngữ sẽ hiểu và không bận tâm đến việc dịch.
Trong tập dữ liệu KDE4, từ này đã được dịch bằng tiếng Pháp một cách chính thống hơn thành "module d'extension":

```py
split_datasets["train"][172]["translation"]
```

```python out
{'en': 'Unable to import %1 using the OFX importer plugin. This file is not the correct format.',
 'fr': "Impossible d'importer %1 en utilisant le module d'extension d'importation OFX. Ce fichier n'a pas un format correct."}
```

Tuy nhiên, mô hình được huấn luyện trước của chúng ta gắn với từ tiếng Anh nhỏ gọn và quen thuộc:

```py
translator(
    "Unable to import %1 using the OFX importer plugin. This file is not the correct format."
)
```

```python out
[{'translation_text': "Impossible d'importer %1 en utilisant le plugin d'importateur OFX. Ce fichier n'est pas le bon format."}]
```

Sẽ rất thú vị khi xem liệu mô hình tinh chỉnh của mình có tiếp thu những đặc điểm đó của tập dữ liệu hay không (cảnh báo spoiler: nó sẽ xảy ra).

<Youtube id="0Oxphw4Q9fo"/>

> [!TIP]
> ✏️ **Đến lượt bạn!** Một từ tiếng Anh khác thường được sử dụng trong tiếng Pháp là "email". Tìm mẫu đầu tiên trong tập dữ liệu huấn luyện sử dụng từ này. Nó được dịch như thế nào? Làm thế nào để mô hình huấn luyện trước dịch cùng một câu tiếng Anh?

### Chuẩn bị dữ liệu

<Youtube id="XAR8jnZZuUs"/>

Bây giờ bạn nên biết điều này: tất cả các văn bản cần được chuyển đổi thành tập hợp các token ID để mô hình có thể hiểu được chúng. Đối với tác vụ này, chúng ta sẽ cần tokenize cả đầu vào và nhãn. Tác vụ đầu tiên của chúng ta là tạo đối tượng `tokenizer`. Như đã lưu ý trước đó, chúng ta sẽ sử dụng mô hình huấn luyện trước từ tiếng Anh sang tiếng Pháp của Marian. Nếu bạn đang thử đoạn mã này với một cặp ngôn ngữ khác, hãy đảm bảo điều chỉnh checkpoint của mô hình. Tổ chức [Helsinki-NLP](https://huggingface.co/Helsinki-NLP) cung cấp hơn một nghìn mô hình bằng nhiều ngôn ngữ.

```python
from transformers import AutoTokenizer

model_checkpoint = "Helsinki-NLP/opus-mt-en-fr"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, return_tensors="tf")
```

You can also replace the `model_checkpoint` with any other model you prefer from the [Hub](https://huggingface.co/models), or a local folder where you've saved a pretrained model and a tokenizer.

> [!TIP]
> 💡 Nếu bạn đang sử dụng trình tokenize đa ngôn ngữ như mBART, mBART-50 hoặc M2M100, bạn sẽ cần đặt mã ngôn ngữ của đầu vào và nhãn của mình trong trình tokenize bằng cách đặt `tokenizer.src_lang` và `tokenizer.tgt_lang` ở bên phải các giá trị.

Việc chuẩn bị dữ liệu của chúng ta khá đơn giản. Chỉ có một điều cần nhớ: bạn xử lý các đầu vào như bình thường, nhưng đối với các nhãn, bạn cần phải bọc tokenizer bên trong trình quản lý ngữ cảnh `as_target_tokenizer()`.

Trình quản lý ngữ cảnh trong Python được giới thiệu với câu lệnh `with` và rất hữu ích khi bạn có hai hoạt động liên quan để thực thi như một cặp. Ví dụ phổ biến nhất về điều này là khi bạn viết hoặc đọc một tệp, thường được thực hiện bên trong một lệnh như:

```
with open(file_path) as f:
    content = f.read()
```

Ở đây, hai hoạt động liên quan được thực hiện như một cặp là các hành động mở và đóng tệp. Đối tượng tương ứng với tệp đã mở `f` chỉ tồn tại bên trong khối được thụt lề dưới dấu `with`; sự mở đầu xảy ra trước khối đó và đóng ở cuối khối.

Trong trường hợp này, trình quản lý ngữ cảnh `as_target_tokenizer()` sẽ đặt tokenizer ở ngôn ngữ đầu ra (ở đây, tiếng Pháp) trước khi khối được thụt lề được thực thi, sau đó đặt nó trở lại bằng ngôn ngữ đầu vào (ở đây, tiếng Anh).

Vì vậy, việc xử lý trước một mẫu trông như thế này:

```python
en_sentence = split_datasets["train"][1]["translation"]["en"]
fr_sentence = split_datasets["train"][1]["translation"]["fr"]

inputs = tokenizer(en_sentence)
with tokenizer.as_target_tokenizer():
    targets = tokenizer(fr_sentence)
```

Nếu chúng ta quên tokenize các nhãn bên trong trình quản lý ngữ cảnh, chúng sẽ được tokenize bởi trình tokenize đầu vào, trong trường hợp mô hình Marian sẽ không hoạt động tốt chút nào:

```python
wrong_targets = tokenizer(fr_sentence)
print(tokenizer.convert_ids_to_tokens(wrong_targets["input_ids"]))
print(tokenizer.convert_ids_to_tokens(targets["input_ids"]))
```

```python out
['▁Par', '▁dé', 'f', 'aut', ',', '▁dé', 've', 'lop', 'per', '▁les', '▁fil', 's', '▁de', '▁discussion', '</s>']
['▁Par', '▁défaut', ',', '▁développer', '▁les', '▁fils', '▁de', '▁discussion', '</s>']
```

Như chúng ta có thể thấy, việc sử dụng trình tokenize tiếng Anh để xử lý trước một câu tiếng Pháp dẫn đến nhiều token hơn, vì trình tokenize không biết bất kỳ từ tiếng Pháp nào (ngoại trừ những từ cũng xuất hiện trong tiếng Anh, như "discussion").

Cả `inputs` và `targets` đều là từ điển với các khóa thông thường của chúng ta (ID đầu vào, attention mask, v.v.), vì vậy bước cuối cùng là đặt `"labels"` bên trong các đầu vào. Chúng ta thực hiện điều này trong chức năng tiền xử lý mà ta sẽ áp dụng trên các tập dữ liệu:

```python
max_input_length = 128
max_target_length = 128


def preprocess_function(examples):
    inputs = [ex["en"] for ex in examples["translation"]]
    targets = [ex["fr"] for ex in examples["translation"]]
    model_inputs = tokenizer(inputs, max_length=max_input_length, truncation=True)

    # Thiết lập tokenizer cho nhãn
    with tokenizer.as_target_tokenizer():
        labels = tokenizer(targets, max_length=max_target_length, truncation=True)

    model_inputs["labels"] = labels["input_ids"]
    return model_inputs
```

Note that we set similar maximum lengths for our inputs and outputs. Since the texts we're dealing with seem pretty short, we use 128.

> [!TIP]
> 💡 Nếu bạn đang sử dụng mô hình T5 (cụ thể hơn là một trong các checkpoint `t5-xxx`), mô hình sẽ mong đợi các đầu vào văn bản có tiền tố cho biết tác vụ đang thực hiện, chẳng hạn như `translate: English to French:`.

> [!WARNING]
> ⚠️ Chúng ta không chú ý đến attention mask của các nhãn, vì mô hình sẽ không mong đợi điều đó. Thay vào đó, các nhãn tương ứng với token đệm phải được đặt thành `-100` để chúng bị bỏ qua trong tính toán mất mát. Điều này sẽ được thực hiện bởi trình đối chiếu dữ liệu của chúng ta sau này vì chúng ta đang áp dụng đệm động, nhưng nếu bạn sử dụng đệm ở đây, bạn nên điều chỉnh chức năng tiền xử lý để đặt tất cả các nhãn tương ứng với token đệm thành `-100`.

Bây giờ chúng ta có thể áp dụng tiền xử lý đó trong một lần trên tất cả các phần của tập dữ liệu của mình:

```py
tokenized_datasets = split_datasets.map(
    preprocess_function,
    batched=True,
    remove_columns=split_datasets["train"].column_names,
)
```

Bây giờ dữ liệu đã được tiền xử lý, chúng ta đã sẵn sàng để tinh chỉnh mô hình tiền xử lý của mình!

{#if fw === 'pt'}

## Tinh chỉnh mô hình với API `Trainer`

Đoạn mã thực sử dụng `Trainer` sẽ giống như trước đây, chỉ với một thay đổi nhỏ: chúng ta sử dụng [`Seq2SeqTrainer`](https://huggingface.co/transformers/main_classes/trainer.html#seq2seqtrainer) tại đây, là một lớp con của `Trainer` sẽ cho phép chúng ta xử lý tốt việc đánh giá, sử dụng phương thức `generate()` để dự đoán kết quả đầu ra từ các đầu vào. Chúng ta sẽ đi sâu vào vấn đề đó chi tiết hơn khi ta nói về tính toán số liệu.

Điều đầu tiên, chúng ta cần một mô hình thực tế để tinh chỉnh. Chúng ta sẽ sử dụng API `AutoModel`:

```py
from transformers import AutoModelForSeq2SeqLM

model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

{:else}

## Tinh chỉnh mô hình với Keras

Điều đầu tiên, chúng ta cần một mô hình thực tế để tinh chỉnh. Chúng ta sẽ sử dụng API `AutoModel`:

```py
from transformers import TFAutoModelForSeq2SeqLM

model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint, from_pt=True)
```

<Tip warning={false}>

💡 Checkpoint `Helsinki-NLP/opus-mt-en-fr` chỉ có trọng số PyTorch, nên bạn sẽ nhận được lỗi nếu bạn cố tải mô hình mà không sử dụng tham số `from_pt=True`, thư viện sẽ tự động tải và chuyển các trọng số Pytorch cho bạn. Như bạn có thể thấy, rất đơn giản để chuyển giữa các khung trong 🤗 Transformers!

</Tip>

{/if}

Lưu ý rằng lần này chúng ta đang sử dụng một mô hình đã được huấn luyện về tác vụ dịch và thực sự có thể được sử dụng, vì vậy không có cảnh báo nào về việc thiếu các trọng số hoặc những trọng số mới được khởi tạo.

### Đối chiếu dữ liệu

Chúng ta sẽ cần một công cụ đối chiếu dữ liệu để xử lý phần đệm cho phân phối động. Chúng ta không thể chỉ sử dụng một `DataCollatorWithPadding` như [Chương 3](/course/ chapter3) trong trường hợp này, bởi vì điều đó chỉ đệm các đầu vào (ID đầu vào, attention mask, và loại token ID). Các nhãn của chúng ta cũng phải được đệm theo chiều dài tối đa có trong nhãn. Và, như đã đề cập trước đây, giá trị đệm được sử dụng để đệm các nhãn phải là `-100` chứ không phải token đệm của trình tokenize, để đảm bảo các giá trị đệm đó bị bỏ qua trong tính toán mất mát.

Tất cả điều này được thực hiện bởi [`DataCollatorForSeq2Seq`](https://huggingface.co/transformers/main_classes/data_collator.html#datacollatorforseq2seq). Giống như `DataCollatorWithPadding`, nó sử dụng `tokenizer` được sử dụng để xử lý trước các đầu vào, nhưng nó cũng lấy `model`. Điều này là do trình đối chiếu dữ liệu này cũng sẽ chịu trách nhiệm chuẩn bị các ID đầu vào của bộ giải mã, là các phiên bản được dịch chuyển của các nhãn với một token đặc biệt ở đầu. Vì sự thay đổi này được thực hiện hơi khác đối với các kiến ​​trúc khác nhau, nên `DataCollatorForSeq2Seq` cần biết đối tượng `model`:

{#if fw === 'pt'}

```py
from transformers import DataCollatorForSeq2Seq

data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)
```

{:else}

```py
from transformers import DataCollatorForSeq2Seq

data_collator = DataCollatorForSeq2Seq(tokenizer, model=model, return_tensors="tf")
```

{/if}

Để kiểm tra điều này trên một số mẫu, chúng ta chỉ cần gọi nó trong danh sách các ví dụ từ bộ huấn luyện được tokenize của mình:

```py
batch = data_collator([tokenized_datasets["train"][i] for i in range(1, 3)])
batch.keys()
```

```python out
dict_keys(['attention_mask', 'input_ids', 'labels', 'decoder_input_ids'])
```

Chúng tôi có thể kiểm tra nhãn đã được đệm đến độ dài tối đa của lô hay chưa, bằng cách sử dụng `-100`:

```py
batch["labels"]
```

```python out
tensor([[  577,  5891,     2,  3184,    16,  2542,     5,  1710,     0,  -100,
          -100,  -100,  -100,  -100,  -100,  -100],
        [ 1211,     3,    49,  9409,  1211,     3, 29140,   817,  3124,   817,
           550,  7032,  5821,  7907, 12649,     0]])
```

Và chúng tôi cũng có thể xem xét các ID đầu vào của bộ giải mã, để biết rằng chúng là các phiên bản được thay đổi của nhãn:

```py
batch["decoder_input_ids"]
```

```python out
tensor([[59513,   577,  5891,     2,  3184,    16,  2542,     5,  1710,     0,
         59513, 59513, 59513, 59513, 59513, 59513],
        [59513,  1211,     3,    49,  9409,  1211,     3, 29140,   817,  3124,
           817,   550,  7032,  5821,  7907, 12649]])
```

Dưới đây là các nhãn cho các phần tử đầu tiên và thứ hai trong tập dữ liệu của mình:

```py
for i in range(1, 3):
    print(tokenized_datasets["train"][i]["labels"])
```

```python out
[577, 5891, 2, 3184, 16, 2542, 5, 1710, 0]
[1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124, 817, 550, 7032, 5821, 7907, 12649, 0]
```

{#if fw === 'pt'}

Chúng ta sẽ truyền `data_collator` vào `Seq2SeqTrainer`. Tiếp theo, chúng ta hãy xem xét chỉ số.

{:else}

Ta có thể sử dụng `data_collator` để chuyển mỗi phần dữ liệu thành `tf.data.Dataset`, sẵn sàng để huấn luyện:

```python
tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
    columns=["input_ids", "attention_mask", "labels"],
    collate_fn=data_collator,
    shuffle=True,
    batch_size=32,
)
tf_eval_dataset = tokenized_datasets["validation"].to_tf_dataset(
    columns=["input_ids", "attention_mask", "labels"],
    collate_fn=data_collator,
    shuffle=False,
    batch_size=16,
)
```

{/if}

### Thước đo

<Youtube id="M05L1DhFqcw"/>

{#if fw === 'pt'}

Tính năng mà `Seq2SeqTrainer` thêm vào lớp cha `Trainer` của nó là khả năng sử dụng phương thức `generate()` trong quá trình đánh giá hoặc dự đoán. Trong quá trình huấn luyện, mô hình sẽ sử dụng `decoder_input_ids` với attention mask đảm bảo nó không sử dụng các token sau token mà nó đang cố gắng dự đoán, để tăng tốc độ huấn luyện. Trong quá trình luận suy, chúng ta sẽ không thể sử dụng những thứ đó vì chúng ta sẽ không có nhãn, vì vậy, bạn nên đánh giá mô hình của mình với cùng một thiết lập.

Như chúng ta đã thấy trong [Chương 1](/course/chapter1/6), bộ giải mã thực hiện luận suy bằng cách dự đoán từng token - một thứ được triển khai phía sau trong 🤗 Transformers bằng phương thức `generate()`. `Seq2SeqTrainer` sẽ cho phép chúng ta sử dụng phương pháp đó để đánh giá nếu chúng ta đặt `predict_with_generate=True`.

{/if}

Chỉ số truyền thống được sử dụng cho bài toán dịch là [điểm BLEU](https://en.wikipedia.org/wiki/BLEU), được giới thiệu trong [một bài báo năm 2002](https://aclanthology.org/P02-1040.pdf) bởi Kishore Papineni và cộng sự. Điểm BLEU đánh giá mức độ gần gũi của bản dịch với nhãn của chúng. Nó không đo lường mức độ dễ hiểu hoặc tính đúng ngữ pháp của các đầu ra được tạo ra của mô hình, nhưng sử dụng các quy tắc thống kê để đảm bảo rằng tất cả các từ trong các đầu ra được tạo cũng xuất hiện trong các nhãn. Ngoài ra, có các quy tắc phạt việc lặp lại các từ giống nhau nếu chúng không được lặp lại trong các nhãn (để tránh mô hình xuất ra các câu như `"the the the"`) và xuất ra các câu ngắn hơn các câu trong nhãn (để tránh mô hình xuất ra các câu như `"the"`).

Một điểm yếu của BLEU là nó mong đợi văn bản đã được tokenize, điều này gây khó khăn cho việc so sánh điểm giữa các mô hình sử dụng các bộ tokenize khác nhau. Vì vậy, thay vào đó, chỉ số được sử dụng phổ biến nhất cho các mô hình dịch điểm chuẩn ngày nay là [SacreBLEU](https://github.com/mjpost/sacrebleu), giải quyết điểm yếu này (và các chỉ số khác) bằng cách chuẩn hóa bước tokenize. Để sử dụng chỉ số này, trước tiên chúng ta cần cài đặt thư viện SacreBLEU:

```py
!pip install sacrebleu
```

Chúng ta có thể tải nó với `evaluate.load()` như chúng ta đã làm trong [Chương 3](/course/chapter3):

```py
import evaluate

metric = evaluate.load("sacrebleu")
```

Chỉ số này sẽ lấy văn bản làm đầu vào và nhãn. Nó được thiết kế để chấp nhận một số nhãn có thể chấp nhận được, vì thường có nhiều bản dịch có thể chấp nhận được của cùng một câu - tập dữ liệu ta đang sử dụng chỉ cung cấp một nhãn, nhưng không hiếm trong NLP để tìm tập dữ liệu cung cấp một số câu dưới dạng nhãn. Vì vậy, các dự đoán phải là một danh sách các câu, nhưng các tham chiếu phải là một danh sách các danh sách các câu.

Hãy thử một mẫu:

```py
predictions = [
    "This plugin lets you translate web pages between several languages automatically."
]
references = [
    [
        "This plugin allows you to automatically translate web pages between several languages."
    ]
]
metric.compute(predictions=predictions, references=references)
```

```python out
{'score': 46.750469682990165,
 'counts': [11, 6, 4, 3],
 'totals': [12, 11, 10, 9],
 'precisions': [91.67, 54.54, 40.0, 33.33],
 'bp': 0.9200444146293233,
 'sys_len': 12,
 'ref_len': 13}
```

Ta nhận được điểm BLEU là 46.75, khá tốt - để tham khảo, mô hình Transformer ban đầu trong bài báo ["Attention Is All You Need"](https://arxiv.org/pdf/1706.03762.pdf) đạt được điểm BLEU là 41.8 cho một tác vụ dịch tương tự giữa tiếng Anh và tiếng Pháp! (Để biết thêm thông tin về các chỉ số riêng lẻ, như `counts` và `bp`, hãy xem [kho lưu trữ SacreBLEU](https://github.com/mjpost/sacrebleu/blob/078c440168c6adc89ba75fe6d63f0d922d42bcfe/sacrebleu/metrics/bleu.py#L74 ).) Mặt khác, nếu chúng ta thử với hai loại dự đoán không tốt (nhiều lần lặp lại hoặc quá ngắn) thường xuất hiện trong các mô hình dịch, chúng ta sẽ nhận được điểm BLEU khá tệ:

```py
predictions = ["This This This This"]
references = [
    [
        "This plugin allows you to automatically translate web pages between several languages."
    ]
]
metric.compute(predictions=predictions, references=references)
```

```python out
{'score': 1.683602693167689,
 'counts': [1, 0, 0, 0],
 'totals': [4, 3, 2, 1],
 'precisions': [25.0, 16.67, 12.5, 12.5],
 'bp': 0.10539922456186433,
 'sys_len': 4,
 'ref_len': 13}
```

```py
predictions = ["This plugin"]
references = [
    [
        "This plugin allows you to automatically translate web pages between several languages."
    ]
]
metric.compute(predictions=predictions, references=references)
```

```python out
{'score': 0.0,
 'counts': [2, 1, 0, 0],
 'totals': [2, 1, 0, 0],
 'precisions': [100.0, 100.0, 0.0, 0.0],
 'bp': 0.004086771438464067,
 'sys_len': 2,
 'ref_len': 13}
```

Điểm số có thể tăng từ 0 đến 100 và càng cao thì càng tốt.

{#if fw === 'tf'}

Để chuyển từ kết quả đầu ra của mô hình thành văn bản mà chỉ số có thể sử dụng, chúng ta sẽ sử dụng phương thức `tokenizer.batch_decode()`. Chúng ta chỉ cần xóa tất cả các `-100` trong các nhãn; tokenizer sẽ tự động làm điều tương tự đối với token đệm. Hãy xác định một hàm sử dụng mô hình và tập dữ liệu của chúng ta và tính toán các số liệu trên đó. Vì việc tạo chuỗi dài có thể chậm, chúng ta lấy mẫu thay thế bộ kiểm định để đảm bảo điều này không chạy mãi mãi:

```py
import numpy as np


def compute_metrics():
    all_preds = []
    all_labels = []
    sampled_dataset = tokenized_datasets["validation"].shuffle().select(range(200))
    tf_generate_dataset = sampled_dataset.to_tf_dataset(
        columns=["input_ids", "attention_mask", "labels"],
        collate_fn=data_collator,
        shuffle=False,
        batch_size=4,
    )
    for batch in tf_generate_dataset:
        predictions = model.generate(
            input_ids=batch["input_ids"], attention_mask=batch["attention_mask"]
        )
        decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
        labels = batch["labels"].numpy()
        labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
        decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
        decoded_preds = [pred.strip() for pred in decoded_preds]
        decoded_labels = [[label.strip()] for label in decoded_labels]
        all_preds.extend(decoded_preds)
        all_labels.extend(decoded_labels)

    result = metric.compute(predictions=all_preds, references=all_labels)
    return {"bleu": result["score"]}
```

{:else}

To get from the model outputs to texts the metric can use, we will use the `tokenizer.batch_decode()` method. We just have to clean up all the `-100`s in the labels (the tokenizer will automatically do the same for the padding token):

```py
import numpy as np


def compute_metrics(eval_preds):
    preds, labels = eval_preds
    # Trong trường hợp mô hình trả về nhiều hơn logit dự đoán
    if isinstance(preds, tuple):
        preds = preds[0]

    decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True)

    # Thay các gía trị -100 trong nhãn vì ta không giải mã chúng
    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)

    # Thực một một xố hậu xủ lý đơn giản
    decoded_preds = [pred.strip() for pred in decoded_preds]
    decoded_labels = [[label.strip()] for label in decoded_labels]

    result = metric.compute(predictions=decoded_preds, references=decoded_labels)
    return {"bleu": result["score"]}
```

{/if}

Bây giờ điều này đã hoàn tất, chúng ta đã sẵn sàng tinh chỉnh mô hình của mình!

### Tinh chỉnh mô hình

Bước đầu tiên là đăng nhập vào Hugging Face để bạn có thể tải kết quả của mình lên Model Hub. Có một chức năng tiện lợi để giúp bạn làm điều này trong notebook:

```python
from huggingface_hub import notebook_login

notebook_login()
```

Thao tác này sẽ hiển thị một tiện ích mà bạn có thể nhập thông tin đăng nhập Hugging Face của mình.

Nếu bạn không làm việc trong notebook, chỉ cần nhập dòng sau vào terminal của bạn:

```bash
huggingface-cli login
```

{#if fw === 'tf'}

Trước khi bắt đầu, hãy xem loại kết quả nào chúng tôi nhận được từ mô hình của mình mà không cần huấn luyện:

```py
print(compute_metrics())
```

```
{'bleu': 33.26983701454733}
```

Khi điều này được thực hiện, chúng ta có thể chuẩn bị mọi thứ cần để biên dịch và huấn luyện mô hình của mình. Lưu ý việc sử dụng `tf.keras.mixed_precision.set_global_policy("mixed_float16")` - điều này sẽ yêu cầu Keras huấn luyện bằng cách sử dụng float16, điều có thể giúp tăng tốc đáng kể trên các GPU hỗ trợ nó (Nvidia 20xx/V100 hoặc mới hơn).

```python
from transformers import create_optimizer
from transformers.keras_callbacks import PushToHubCallback
import tensorflow as tf

# Số bước huấn luyện là số lượng mẫu trong tập dữ liệu, chia cho kích thước lô sau đó nhân
# với tổng số epoch. Lưu ý rằng tf_train_dataset ở đây là tf.data.Dataset theo lô,
# không phải là Hugging Face Dataset ban đầu, vì vậy len() của nó vốn là num_samples // batch_size.

num_epochs = 3
num_train_steps = len(tf_train_dataset) * num_epochs

optimizer, schedule = create_optimizer(
    init_lr=5e-5,
    num_warmup_steps=0,
    num_train_steps=num_train_steps,
    weight_decay_rate=0.01,
)
model.compile(optimizer=optimizer)

# Huấn luyện trong mixed-precision float16
tf.keras.mixed_precision.set_global_policy("mixed_float16")
```

Tiếp theo, chúng ta xác định một `PushToHubCallback` để tải mô hình lên Hub trong quá trình huấn luyện, như chúng ta đã thấy trong [phần 2]((/course/chapter7/2)), và sau đó chúng ta chỉ cần điều chỉnh mô hình với lệnh gọi lại đó:

```python
from transformers.keras_callbacks import PushToHubCallback

callback = PushToHubCallback(
    output_dir="marian-finetuned-kde4-en-to-fr", tokenizer=tokenizer
)

model.fit(
    tf_train_dataset,
    validation_data=tf_eval_dataset,
    callbacks=[callback],
    epochs=num_epochs,
)
```

Lưu ý rằng bạn có thể chỉ định tên của kho lưu trữ mà bạn muốn đẩy lên bằng tham số `hub_model_id` (cụ thể là bạn sẽ phải sử dụng tham số này để đẩy lên một tổ chức). Ví dụ: khi chúng tôi đẩy mô hình vào tổ chức [`huggingface-course`](https://huggingface.co/huggingface-course), chúng ta đã thêm `hub_model_id="huggingface-course/marian-finetuned-kde4-en-to-fr"` thành `Seq2SeqTrainingArguments`. Theo mặc định, kho lưu trữ được sử dụng sẽ nằm trong không gian tên của bạn và được đặt tên theo thư mục đầu ra mà bạn đã đặt, vì vậy ở đây nó sẽ là `"sgugger/marian-finetuned-kde4-en-to-fr"` (là mô hình mà chúng tôi đã liên kết với ở đầu phần này).

> [!TIP]
> 💡 Nếu thư mục đầu ra bạn đang sử dụng đã tồn tại, nó cần phải là bản sao cục bộ của kho lưu trữ mà bạn muốn đẩy đến. Nếu không, bạn sẽ gặp lỗi khi gọi `model.fit()` và sẽ cần đặt tên mới.

Cuối cùng, hãy xem các chỉ số của chúng ta trông như thế nào khi quá trình huấn luyện đã kết thúc:

```py
print(compute_metrics())
```

```
{'bleu': 57.334066271545865}
```

Ở giai đoạn này, bạn có thể sử dụng tiện ích luận suy trên Model Hub để kiểm tra mô hình của mình và chia sẻ với bạn bè. Bạn đã tinh chỉnh thành công một mô hình trong tác vụ dịch - xin chúc mừng!

{:else}

Khi điều này được thực hiện, chúng ta có thể xác định `Seq2SeqTrainingArguments`. Giống như đối với `Trainer`, chúng ta sử dụng một lớp con của `TrainingArguments` chứa thêm một số trường:

```python
from transformers import Seq2SeqTrainingArguments

args = Seq2SeqTrainingArguments(
    f"marian-finetuned-kde4-en-to-fr",
    evaluation_strategy="no",
    save_strategy="epoch",
    learning_rate=2e-5,
    per_device_train_batch_size=32,
    per_device_eval_batch_size=64,
    weight_decay=0.01,
    save_total_limit=3,
    num_train_epochs=3,
    predict_with_generate=True,
    fp16=True,
    push_to_hub=True,
)
```

Ngoài các siêu tham số thông thường (như tốc độ học, số epoch, kích thước lô và một số phân rã trọng số), đây là một số thay đổi so với những gì chúng ta đã thấy trong các phần trước:

- Chúng ta không đặt bất kỳ đánh giá thường xuyên nào, vì quá trình đánh giá sẽ mất một khoảng thời gian; chúng ta sẽ chỉ đánh giá mô hình của mình một lần trước khi huấn luyện và sau đó.
- Chúng ta đặt `fp16=True`, giúp tăng tốc quá trình huấn luyện trên các GPU hiện đại.
- Chúng ta đặt `predict_with_generate=True`, như đã thảo luận ở trên.
- Chúng ta sử dụng `push_to_hub=True` để tải mô hình lên Hub vào cuối mỗi epoch.

Lưu ý rằng bạn có thể chỉ định tên đầy đủ của kho lưu trữ mà bạn muốn đẩy đến bằng tham số `hub_model_id` (đặc biệt, bạn sẽ phải sử dụng tham số này để đẩy đến một tổ chức). Ví dụ: khi chúng ta đẩy mô hình vào tổ chức [`huggingface-course`](https://huggingface.co/huggingface-course), chúng ta đã thêm `hub_model_id="huggingface-course/marian-finetuned-kde4-en-to-fr"` thành `Seq2SeqTrainingArguments`. Theo mặc định, kho lưu trữ được sử dụng sẽ nằm trong không gian tên của bạn và được đặt tên theo thư mục đầu ra mà bạn đã đặt, vì vậy trong trường hợp của chúng tôi, nó sẽ là `"sgugger/marian-finetuned-kde4-en-to-fr"` (là mô hình chúng tôi liên kết đến ở đầu phần này).

> [!TIP]
> 💡 Nếu thư mục đầu ra bạn đang sử dụng đã tồn tại, nó cần phải là bản sao cục bộ của kho lưu trữ mà bạn muốn đẩy đến. Nếu không, bạn sẽ gặp lỗi khi xác định `Seq2SeqTrainer` của mình và sẽ cần đặt tên mới.

Cuối cùng, ta chỉ cần truyền mọi thứ cho `Seq2SeqTrainer`:

```python
from transformers import Seq2SeqTrainer

trainer = Seq2SeqTrainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
    compute_metrics=compute_metrics,
)
```

Trước khi huấn luyện, trước tiên chúng ta sẽ xem xét điểm mà mô hình của chúng ta nhận được, để kiểm tra kỹ xem chúng ta có đang không làm mọi thứ tồi tệ hơn với việc tinh chỉnh của chúng ta hay không. Lệnh này sẽ mất một chút thời gian, vì vậy bạn có thể uống một ly cà phê trong khi nó thực thi:

```python
trainer.evaluate(max_length=max_target_length)
```

```python out
{'eval_loss': 1.6964408159255981,
 'eval_bleu': 39.26865061007616,
 'eval_runtime': 965.8884,
 'eval_samples_per_second': 21.76,
 'eval_steps_per_second': 0.341}
```

Điểm BLEU là 39 không quá tệ, điều này phản ánh thực tế là mô hình của chúng ta đã rất giỏi trong việc dịch các câu tiếng Anh sang tiếng Pháp.

Tiếp theo là huấn luyện, cũng sẽ mất một chút thời gian:

```python
trainer.train()
```

Lưu ý rằng trong khi quá trình huấn luyện diễn ra, mỗi khi mô hình được lưu (ở đây, mỗi epoch), nó sẽ được tải lên Hub ở chế độ nền. Bằng cách này, bạn sẽ có thể tiếp tục huấn luyện của mình trên một máy khác nếu cần.

Sau khi huấn luyện xong, chúng ta đánh giá lại mô hình của mình - hy vọng chúng ta sẽ thấy một số cải thiện trong điểm BLEU!

```py
trainer.evaluate(max_length=max_target_length)
```

```python out
{'eval_loss': 0.8558505773544312,
 'eval_bleu': 52.94161337775576,
 'eval_runtime': 714.2576,
 'eval_samples_per_second': 29.426,
 'eval_steps_per_second': 0.461,
 'epoch': 3.0}
```

Đó là một cải tiến hơn gần 14 điểm, thật tuyệt vời.

Cuối cùng, chúng ta sử dụng phương thức `push_to_hub()` để đảm bảo tải lên phiên bản mới nhất của mô hình. `Trainer` cũng soạn thảo một thẻ mô hình với tất cả các kết quả đánh giá và tải nó lên. Thẻ mô hình này chứa siêu dữ liệu giúp Model Hub chọn tiện ích con cho bản trình diễn luận suy. Thông thường, không cần phải nói bất cứ điều gì vì nó có thể suy ra tiện ích con phù hợp từ lớp mô hình, nhưng trong trường hợp này, cùng một lớp mô hình có thể được sử dụng cho tất cả các loại vấn đề chuỗi sang chuỗi, vì vậy chúng ta chỉ định đó là một mô hình dịch:

```py
trainer.push_to_hub(tags="translation", commit_message="Training complete")
```
Lệnh này trả về URL của cam kết mà nó vừa thực hiện, nếu bạn muốn kiểm tra nó:

```python out
'https://huggingface.co/sgugger/marian-finetuned-kde4-en-to-fr/commit/3601d621e3baae2bc63d3311452535f8f58f6ef3'
```

Ở giai đoạn này, bạn có thể sử dụng tiện ích luận suy trên Model Hub để kiểm tra mô hình của mình và chia sẻ với bạn bè. Bạn đã tinh chỉnh thành công một mô hình trong tác vụ dịch - xin chúc mừng!

Nếu bạn muốn tìm hiểu sâu hơn một chút về vòng lặp huấn luyện, bây giờ chúng tôi sẽ hướng dẫn bạn cách thực hiện điều tương tự bằng cách sử dụng 🤗 Accelerate.

{/if}

{#if fw === 'pt'}

## Một vòng huấn luyện tùy chỉnh

Bây giờ chúng ta hãy xem toàn bộ vòng lặp huấn luyện, vì vậy bạn có thể dễ dàng tùy chỉnh các phần bạn cần. Nó sẽ trông rất giống những gì chúng ta đã làm trong [phần 2](/course/chapter7/2) và [Chương 3](/course/chapter3/4).

### Chuẩn bị mọi thứ cho qua trình huấn luyện

Bạn đã thấy tất cả điều này một vài lần rồi, vì vậy chúng ta sẽ xem qua đoạn mã khá nhanh. Đầu tiên, chúng ta sẽ xây dựng các `DataLoader` từ các tập dữ liệu của mình, sau khi đặt các tập dữ liệu thành định dạng` "torch" `để chúng ta nhận được các tensor PyTorch:

```py
from torch.utils.data import DataLoader

tokenized_datasets.set_format("torch")
train_dataloader = DataLoader(
    tokenized_datasets["train"],
    shuffle=True,
    collate_fn=data_collator,
    batch_size=8,
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], collate_fn=data_collator, batch_size=8
)
```

Tiếp theo, chúng ta khôi phục mô hình của mình, để đảm bảo rằng chúng ta không tiếp tục tinh chỉnh từ trước mà bắt đầu lại từ mô hình đã được huấn luyện trước:

```py
model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

Then we will need an optimizer:

```py
from torch.optim import AdamW

optimizer = AdamW(model.parameters(), lr=2e-5)
```

Khi chúng ta có tất cả các đối tượng đó, chúng ta có thể gửi chúng đến phương thức `accelerator.prepare()`. Hãy nhớ rằng nếu bạn muốn huấn luyện về TPU trong notebook Colab, bạn sẽ cần chuyển tất cả mã này vào một hàm huấn luyện và điều đó sẽ không thực thi bất kỳ ô nào khởi tạo một `Accelerator`.

```py
from accelerate import Accelerator

accelerator = Accelerator()
model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
    model, optimizer, train_dataloader, eval_dataloader
)
```

Bây giờ, chúng ta đã gửi `train_dataloader` của mình tới `accelerator.prepare()`, chúng ta có thể sử dụng độ dài của nó để tính số bước huấn luyện. Hãy nhớ rằng chúng ta phải luôn làm điều này sau khi chuẩn bị dataloader, vì phương thức đó sẽ thay đổi độ dài của `DataLoader`. Chúng ta sử dụng một lịch trình tuyến tính cổ điển từ tốc độ học đến 0:

```py
from transformers import get_scheduler

num_train_epochs = 3
num_update_steps_per_epoch = len(train_dataloader)
num_training_steps = num_train_epochs * num_update_steps_per_epoch

lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)
```

Cuối cùng, để đẩy mô hình của mình lên Hub, chúng ta sẽ cần tạo một đối tượng `Repository` trong một thư mục đang làm việc. Đầu tiên hãy đăng nhập vào Hugging Face Hub, nếu bạn chưa đăng nhập. Chúng ta sẽ xác định tên kho lưu trữ từ ID mô hình mà chúng ta muốn cung cấp cho mô hình của mình (vui lòng thay thế `repo_name` bằng sự lựa chọn của riêng bạn; nó chỉ cần chứa tên người dùng của bạn, đó là những gì hàm `get_full_repo_name()` thực hiện):

```py
from huggingface_hub import Repository, get_full_repo_name

model_name = "marian-finetuned-kde4-en-to-fr-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
```

```python out
'sgugger/marian-finetuned-kde4-en-to-fr-accelerate'
```

Sau đó, chúng ta có thể sao chép kho lưu trữ đó trong một thư mục cục bộ. Nếu nó đã tồn tại, thư mục cục bộ này phải là bản sao của kho lưu trữ mà chúng ta đang làm việc:

```py
output_dir = "marian-finetuned-kde4-en-to-fr-accelerate"
repo = Repository(output_dir, clone_from=repo_name)
```

Bây giờ chúng ta có thể tải lên bất cứ thứ gì chúng ta lưu trong `output_dir` bằng cách gọi phương thức `repo.push_to_hub()`. Điều này sẽ giúp chúng ta tải lên các mô hình trung gian ở cuối mỗi epoch.

### Vòng lặp huấn luyện

Bây giờ chúng ta đã sẵn sàng để viết vòng lặp huấn luyện đầy đủ. Để đơn giản hóa phần đánh giá của nó, chúng ta định nghĩa hàm `postprocess()` này lấy các dự đoán và nhãn và chuyển đổi chúng thành danh sách các chuỗi mà đối tượng `metric` kì vọng:

```py
def postprocess(predictions, labels):
    predictions = predictions.cpu().numpy()
    labels = labels.cpu().numpy()

    decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)

    # Thay -100 trong nhãn vì ta không thế giải mã chúng.
    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)

    # Thực hiện một số hậu xử lý đơn giản
    decoded_preds = [pred.strip() for pred in decoded_preds]
    decoded_labels = [[label.strip()] for label in decoded_labels]
    return decoded_preds, decoded_labels
```

Vòng lặp huấn luyện trông rất giống với các vòng lặp trong [phần 2](/course/chapter7/2) và [Chương 3](/course/chapter3), với một vài điểm khác biệt trong phần đánh giá - vì vậy hãy tập trung vào điều đó!

Điều đầu tiên cần lưu ý là chúng ta sử dụng phương thức `generate()` để tính toán các dự đoán, nhưng đây là một phương thức trên mô hình cơ sở, không phải mô hình được bao bọc 🤗 Accelerate được tạo trong phương thức `prepare()` . Đó là lý do tại sao chúng ta mở mô hình trước, sau đó gọi phương thức này.

Điều thứ hai là, giống như với [phân loại token](/course/chapter7/2), hai quy trình có thể đã đêm các đầu vào và nhãn thành các hình dạng khác nhau, vì vậy chúng tôi sử dụng `accelerator.pad_across_processes()` để đưa ra các dự đoán và nhãn cùng một hình dạng trước khi gọi phương thức `gather()` . Nếu chúng ta không làm điều này, đánh giá sẽ bị lỗi hoặc bị treo vĩnh viễn.

```py
from tqdm.auto import tqdm
import torch

progress_bar = tqdm(range(num_training_steps))

for epoch in range(num_train_epochs):
    # Huấn luyện
    model.train()
    for batch in train_dataloader:
        outputs = model(**batch)
        loss = outputs.loss
        accelerator.backward(loss)

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)

    # Đánh giá
    model.eval()
    for batch in tqdm(eval_dataloader):
        with torch.no_grad():
            generated_tokens = accelerator.unwrap_model(model).generate(
                batch["input_ids"],
                attention_mask=batch["attention_mask"],
                max_length=128,
            )
        labels = batch["labels"]

        # Cần đệm dự đoán và nhãn để dễ gom lại
        generated_tokens = accelerator.pad_across_processes(
            generated_tokens, dim=1, pad_index=tokenizer.pad_token_id
        )
        labels = accelerator.pad_across_processes(labels, dim=1, pad_index=-100)

        predictions_gathered = accelerator.gather(generated_tokens)
        labels_gathered = accelerator.gather(labels)

        decoded_preds, decoded_labels = postprocess(predictions_gathered, labels_gathered)
        metric.add_batch(predictions=decoded_preds, references=decoded_labels)

    results = metric.compute()
    print(f"epoch {epoch}, BLEU score: {results['score']:.2f}")

    # Lưu và tải
    accelerator.wait_for_everyone()
    unwrapped_model = accelerator.unwrap_model(model)
    unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
    if accelerator.is_main_process:
        tokenizer.save_pretrained(output_dir)
        repo.push_to_hub(
            commit_message=f"Training in progress epoch {epoch}", blocking=False
        )
```

```python out
epoch 0, BLEU score: 53.47
epoch 1, BLEU score: 54.24
epoch 2, BLEU score: 54.44
```

Khi điều này được thực hiện, bạn sẽ có một mô hình có kết quả khá giống với mô hình được huấn luyện với `Seq2SeqTrainer`. Bạn có thể kiểm tra đoạn mã mà chúng ta đã huấn luyện bằng cách sử dụng mã này tại [*huggingface-course/marian-finetuned-kde4-en-to-fr-accelerate*](https://huggingface.co/huggingface-course/marian-finetuned-kde4-en-to-fr-accelerate). Và nếu bạn muốn kiểm tra bất kỳ tinh chỉnh nào đối với vòng lặp huấn luyện, bạn có thể trực tiếp thực hiện chúng bằng cách chỉnh sửa đoạn mã được hiển thị ở trên!

{/if}

## Sử dụng mô hình tinh chỉnh

Chúng tôi đã chỉ cho bạn cách bạn có thể sử dụng mô hình mà ta đã tinh chỉnh trên Model Hub bằng tiện ích luận suy. Để sử dụng nó cục bộ trong một `pipeline`, chúng ta chỉ cần chỉ định mã định danh mô hình thích hợp:

```py
from transformers import pipeline

# Thay nó với checkpoint của bạn
model_checkpoint = "huggingface-course/marian-finetuned-kde4-en-to-fr"
translator = pipeline("translation", model=model_checkpoint)
translator("Default to expanded threads")
```

```python out
[{'translation_text': 'Par défaut, développer les fils de discussion'}]
```

Đúng như mong đợi, mô hình được huấn luyện trước của chúng ta đã điều chỉnh kiến thức của nó cho phù hợp với kho ngữ liệu mà chúng ta đã tinh chỉnh và thay vì để nguyên từ "thread" trong tiếng Anh, giờ đây nó đã dịch nó sang phiên bản chính thức tiếng Pháp. Đối với "plugin" cũng vậy:

```py
translator(
    "Unable to import %1 using the OFX importer plugin. This file is not the correct format."
)
```

```python out
[{'translation_text': "Impossible d'importer %1 en utilisant le module externe d'importation OFX. Ce fichier n'est pas le bon format."}]
```

Một ví dụ tuyệt vời khác về thích ứng chuyện môn!

> [!TIP]
> ✏️ **Đến lượt bạn!** Mô hình trả về cái gì với từ "email" bạn xác định trước đó?
